#include "widget.h"
#include "ui_widget.h"

#include <QTcpSocket>
#include <QMessageBox>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 1. 修改窗口标题
    this->setWindowTitle("服务端");

    // 2. 创建 QTcpServer 的实例
    tcpserver = new QTcpServer(this);

    // 3. 通过信号槽，指定连接处理函数
    connect(tcpserver, &QTcpServer::newConnection, this, &Widget::processConnection);

    // 4. 绑定并监听端口号。一定要确保准备工作重分类，再真正开张营业
    bool ret = tcpserver->listen(QHostAddress::Any, 9090);
    if(!ret){
        QMessageBox::critical(this, "服务器启动失败!", tcpserver->errorString());
    }
}

Widget::~Widget()
{
    delete ui;
}

// 更严谨的做法,应该是每次收到的数据都给放到一个大的字节数组缓冲区中,并且提前约定好应用层协议的格式(分隔符?长度? 其他办法?)
// 再按照协议格式对缓冲区数据进行更细致的解析处理~~
void Widget::processConnection()
{
    // 1. 通过 tcpserver 拿到一个socket 对象，通过这个对象来和客户端进行通信
        // 每个客户端都有一个这样的对象，存在N个，
        // 会造成文件描述符、内存泄露
        // 所以需要及时释放
    QTcpSocket* clientSocket = tcpserver->nextPendingConnection();
    QString log = QString("[") + clientSocket->peerAddress().toString() + ":" + QString::number(clientSocket->peerPort()) + "] 客⼾端上线!";
    ui->listWidget->addItem(log);

    // 2. 通过信号槽，来处理客户端发来请求的情况
    connect(clientSocket, &QTcpSocket::readyRead, this, [=](){
        // a) 读取出请求数据，此处 readall 返回的是 QByteArray，通过赋值转成 QString
        QString request = clientSocket->readAll();
        // b) 根据请求处理响应
        const QString& response = process(request);
        // c) 把响应写回到客户端中
        clientSocket->write(response.toUtf8());
        // d) 把上述信息记录到日志中
        QString log = QString("[") + clientSocket->peerAddress().toString() + ":" + QString::number(clientSocket->peerPort()) + "] req: " +
                      request + ", resp: " + response;
        ui->listWidget->addItem(log);
    });

    // 3. 通过信号槽, 处理断开连接的情况
    connect(clientSocket, &QTcpSocket::disconnected, this, [=](){
        QString log = QString("[") + clientSocket->peerAddress().toString() + ":" + QString::number(clientSocket->peerPort()) + "] 客⼾端下线!";
                      ui->listWidget->addItem(log);
        // 删除 clientSocket
            // 这个操作，不是立即销毁，而是下一轮事件循环中销毁
        clientSocket->deleteLater();
    });
}

QString Widget::process(const QString request)
{
    return request;
}

